package com.example.demo.controller;

import com.example.demo.entity.FileMeta;
import com.example.demo.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import sun.misc.Contended;

import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: DELL
 * Date: 2023-06-30
 * Time: 19:16
 */

// 用户对于磁盘内的操作
@Controller
public class FileController {
    @Autowired
    private FileService fileService;

    /**
     * 通过该方法针对 basePath 描述的目录进行扫描.
     * 把其中的文件和子目录都扫描进去, 并存储到数据库中
     *
     * @param basePath
     */
    public void scanAll(File basePath) throws SQLException {

        System.out.println("[File] scanAll 开始 : " );
        long startTime = System.currentTimeMillis();

        // 调用单线程扫描
        scanAllByThread(basePath);
        long endTime = System.currentTimeMillis();
        System.out.println("[File] scanAll 结束 : " + (endTime - startTime) + "ms");

    }

    /**
     * 实现单线程扫描
     */
    public void scanAllByThread(File basePath) throws SQLException {

        // 判断 basePath 不是目录不进行遍历
        if(!basePath.isDirectory()) {
            return;
        }

        // 调用方法对当前指定路径的目录进行扫描
        scan(basePath);

        // 当前目录中存在子目录, 还需要对这个子目录也进行扫描
        // 列出当前目录下的所有文件/目录
        File[] files = basePath.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        for(File file : files) {
            if(file.isDirectory()) {
                // 是目录继续遍历
                scanAllByThread(file);
            }
        }

    }

    /**
     * 针对一个目录进行扫描. 只针对 path 目录下的文件进行扫描
     * 把 path 目录下的文件和子目录都扫描出来, 并且保存到数据库中
     * 如果 path 中有子目录, 那么我们只是添加这个子目录的路径, 并不会去进一步扫描这个子目录里的其他东西
     * 如果需要再进一步扫描这个子目录里的其他东西, 则需要把子目录的 path 传进来
     *
     * @param path 这是一个名为 path 的文件对象, 并非真实的路径, 需要通过方法来获取
     */
    public void scan(File path) throws SQLException {
        // 打印扫描的绝对路径, 观察执行过程
        System.out.println("[File] 扫描路径为 : " + path.getAbsolutePath());

        // 1. 列出系统上的真实文件/目录
        List<FileMeta> scanned = new ArrayList<>();
        File[] files = path.listFiles(); // 列出系统上真实的文件目录
        if (files != null) {
            // 不是空目录
            for (File file : files) {
                // 添加这个真实文件
                scanned.add(new FileMeta(file));
            }
        }


        // TODO : contains 底层调用的是 equals 方法进行比较, 默认比较的是两个对象的地址( 比较对象的身份是否相同 )
        // 这就会有一个问题, scanned 和 saved 是两个地方 new 的不同对象, 肯定地址不相同, 因此比较一定是错误的, 需要重写比较规则

        // 2. 列举出数据库中当前指定目录的内容
        List<FileMeta> saved = fileService.searchByPath(path.getPath());

        // 3. 找出指定路径下的 scanned 中没有的, 而 saved 数据库中有的进行删除
        List<FileMeta> fileDelete = new ArrayList<>(); // 存储找到的没有的数据库中有而本地上没有的文件

        // 遍历数据库文件看本地文件是否包含数据库文件, 不包含则删除数据库中这些不包含的文件
        for (FileMeta fileMeta : saved) {
            // 本地系统文件不包含数据库中的那些文件
            if (!scanned.contains(fileMeta)) {
                // 进行存储到要删除的集合中最后进行批量删除
                fileDelete.add(fileMeta);
            }
        }

        fileService.delete(fileDelete);

        // 4. 找出系统中有而数据库中没有的进行添加
        List<FileMeta> forAdd = new ArrayList<>();

        // 遍历系统文件看数据库中是否包含系统文件, 不包含则加入, 最后将这些不包含的文件添加到数据库
        for (FileMeta fileMeta : scanned) {
            if (!saved.contains(fileMeta)) {
                forAdd.add(fileMeta);
            }
        }

        fileService.add(forAdd);
    }
}
